#include "camera.h"
#include "math.h"
#include <iostream>
#include <QMessageBox>


#define SQR(x) (x*x)

#define NULL_VECTOR F3dVector(0.0f,0.0f,0.0f)

SF3dVector F3dVector ( GLfloat x, GLfloat y, GLfloat z )
{
    SF3dVector tmp;
    tmp.x = x;
    tmp.y = y;
    tmp.z = z;
    return tmp;
}

GLfloat GetF3dVectorLength( SF3dVector * v)
{
    return (GLfloat)(sqrt(SQR(v->x)+SQR(v->y)+SQR(v->z)));
}

SF3dVector Normalize3dVector( SF3dVector v)
{
    SF3dVector res;
    float l = GetF3dVectorLength(&v);
    if (l == 0.0f) return NULL_VECTOR;
    res.x = v.x / l;
    res.y = v.y / l;
    res.z = v.z / l;
    return res;
}

SF3dVector operator+ (SF3dVector v, SF3dVector u)
{
    SF3dVector res;
    res.x = v.x+u.x;
    res.y = v.y+u.y;
    res.z = v.z+u.z;
    return res;
}
SF3dVector operator- (SF3dVector v, SF3dVector u)
{
    SF3dVector res;
    res.x = v.x-u.x;
    res.y = v.y-u.y;
    res.z = v.z-u.z;
    return res;
}


SF3dVector operator* (SF3dVector v, float r)
{
    SF3dVector res;
    res.x = v.x*r;
    res.y = v.y*r;
    res.z = v.z*r;
    return res;
}

SF3dVector CrossProduct (SF3dVector * u, SF3dVector * v)
{
    SF3dVector resVector;
    resVector.x = u->y*v->z - u->z*v->y;
    resVector.y = u->z*v->x - u->x*v->z;
    resVector.z = u->x*v->y - u->y*v->x;

    return resVector;
}
float operator* (SF3dVector v, SF3dVector u)	//dot product
{
    return v.x*u.x+v.y*u.y+v.z*u.z;
}

bool CCamera::isOutOfBounds(SF3dVector pos)
{
    if(pos.x<maxX && pos.x>minX && pos.y<maxY && pos.y>minY && pos.z<maxZ && pos.z>minZ)
    {
        return false;
    }
    return true;
}

CCamera::CCamera()
{
    //Init
    Position = F3dVector (0.0, 0.0,	0.0);
    ViewDir = F3dVector( 0.0, 0.0, -1.0);
    RightVector = F3dVector (1.0, 0.0, 0.0);
    UpVector = F3dVector (0.0, 1.0, 0.0);

    //Only to be sure:
    RotatedX = RotatedY = RotatedZ = 0.0;
}

void CCamera::Move (SF3dVector Direction)
{
    Position = Position + Direction;
}

void CCamera::RotateX (GLfloat Angle)
{
    RotatedX += Angle;

    //Rotate
    ViewDir = Normalize3dVector(ViewDir*cos(Angle*PIdiv180)
                                + UpVector*sin(Angle*PIdiv180));
}

void CCamera::RotateY (GLfloat Angle)
{
    RotatedY += Angle;

    ViewDir = Normalize3dVector(ViewDir*cos(Angle*PIdiv180)
                                - RightVector*sin(Angle*PIdiv180));

    RightVector = CrossProduct(&ViewDir, &UpVector);
}

void CCamera::RotateZ (GLfloat Angle)
{
    RotatedZ += Angle;

    RightVector = Normalize3dVector(RightVector*cos(Angle*PIdiv180)
                                    + UpVector*sin(Angle*PIdiv180));

    UpVector = CrossProduct(&ViewDir, &RightVector)*-1;
}

void CCamera::Render( void )
{
    SF3dVector ViewPoint = Position+ViewDir;
    gluLookAt(Position.x,Position.y,Position.z, ViewPoint.x,ViewPoint.y,ViewPoint.z,UpVector.x,UpVector.y,UpVector.z);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
      glLoadIdentity();
    glLightModelf(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR);
      // glEnable(GL_LIGHT0);
    float noAmbient[] = {0.0f, 0.0f, 0.2f, 1.0f};       //low ambient light
    float diffuse[]   = {0.0f, 0.0f, 1.0f, 1.0f};
    float position[]  = {0,0,0,1.0f};

    //properties of the light
    glLightfv(GL_LIGHT0, GL_AMBIENT, noAmbient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, position);
    float bluish[] = { 0.3f, 0.3f, 0.7f, 1 };

    glLightfv(GL_LIGHT0, GL_SPECULAR, bluish);

    float direction[]  = {0,0,-1,1.0f};
    //           //spot direction
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, direction);
    //angle of the cone light emitted by the spot : value between 0 to 180
    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 15.0f);
    //exponent propertie defines the concentration of the light
    glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 30.0f);

    //light attenuation (default values used here : no attenuation with the distance)
    glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0f);
    glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f);
    glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0f);
    glPopMatrix();
}

void CCamera::MoveForward( GLfloat Distance )
{
    SF3dVector temp=Position;
    Position = Position + (ViewDir*-Distance);
    if(isOutOfBounds(Position))
    {
        Position=temp;
    }
}

void CCamera::StrafeRight ( GLfloat Distance )
{
    SF3dVector temp=Position;
    Position = Position + (RightVector*Distance);
    if(isOutOfBounds(Position))
    {
        Position=temp;
    }
}

void CCamera::MoveUpward( GLfloat Distance )
{
    SF3dVector temp=Position;
    Position = Position + (UpVector*Distance);
    if(isOutOfBounds(Position))
    {
        Position=temp;
    }
}

